package com.wang.define;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * User: bohao.wang
 * Date: 12-4-7
 * Time: 下午5:25
 */
public class Entities {
    ArrayList<Entity> list = new ArrayList<Entity>();
    class Entity {
        public int type;
        public String name;
        public String literal;
        public String refer;   // refer是literal中出现的引用
        public int declared = 0;
        Entity(int type, String name, String literal){
            this.name = name;
            this.type = type;
            this.literal = literal;
            this.refer = getRefer(literal);
        }

        String getRefer(String literal) {
            Pattern p = Pattern.compile("&(.+?);");
            Matcher m = p.matcher(literal);
            StringBuilder sb = new StringBuilder();
            while(m.find()) {
                if(sb.length() == 0) sb.append(m.group(1));
                else {
                    sb.append("|");
                    sb.append(m.group(1));
                }
            }
            return sb.toString();
        }
    }

    public Entities() {
        add(generalInternal, "lt", "<");
        add(generalInternal, "gt", ">");
        add(generalInternal, "amp", "&");
        add(generalInternal, "apos", "%");
        add(generalInternal, "quot", "\"");
    }

    public static final int
            parameter = 1, generalInternal = 2,
            externalParsed = 3, unparsed = 4;

    // 对添加的已析实体进行递归检测，看看是否有自引用
    public void add(int type, String name, String literal){
        Entity entity = new  Entity(type, name, literal);
        if( (type == generalInternal || type == externalParsed) &&
             hasRecursion(entity, entity.refer, new HashSet<String>()) )
            throw new RuntimeException();
        list.add(entity);
    }

    public boolean hasRecursion(Entity e, String refer, Set<String> set) {
        if( refer.length() == 0) return false;
        for(String str : refer.split("\\|")) {
            if(e.name.equals(str)) return true;
            if(!set.contains(str)) {
                set.add(str);
                Entity t = findEntity(str);
                if( t != null && hasRecursion(e, t.refer , set )) return true;
            }
        }
        return false;
    }

    // 只检查内部实体引用
    // 以为不存在环，所以不要set了
    public boolean hasDeclared(String name) {
        Entity entity = findEntity(name);
        if(entity == null ) return false;
        if(entity.type != generalInternal)return true;
        if(entity.declared == 0) {
            String refer = entity.refer;
            if(refer.length() == 0) entity.declared = 1;
            else {
                entity.declared  = 1;
                for(String child : refer.split("\\|")) {
                    if(!hasDeclared(child)) {entity.declared = 2;break;}
                }
            }
        }
        return entity.declared == 1; // 1 = true 2 = false
    }

    public boolean hasExternal(String name) {
        Entity entity = findEntity(name);
        if(entity == null) return false;
        if(entity.type == externalParsed ) return true;
        if(entity.refer.length() == 0) return false;
        for(String child : entity.refer.split("\\|")) {
            if(hasExternal(child)) return true;
        }
        return false;
    }

    public boolean hasLt(String name) {
        Entity entity = findEntity(name);
        // 这不可能出现
        if(entity == null) return false;
        if(entity.literal.contains("<")) return true;
        if(entity.refer.length()  == 0)return false;
        for(String child : entity.refer.split("\\|")) {
            if(hasLt(child)) return true;
        }
        return false;
    }

    private Entity findEntity(String name) {
        for(Entity e : list) {
            if(e.name.equals(name))return  e;
        }
        return null;
    }

    public void listAll() {
        for(Entity e : list) {
            System.out.println(e.type + " " + e.name + " " + e.literal
                    + " " + e.refer);
        }
    }

    public boolean isUnparsed(String name) {
        for(Entity e : list) {
            if(e.name.equals(name) && e.type == unparsed)
                return true;
        }
        return false;
    }


    public static void main(String[] args) {
        for(String str : "123".split("\\|")) {
            System.out.println("1");
        }
    }
}

